package rlib

import (
	"encoding/json"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/golang/glog"
)

type ApiClient struct {
	addr string

	C *http.Client
}

func (ac *ApiClient) Init(nodeName string) {
	glog.Infof("apiclient init|nodeName=%s", nodeName)
	ac.C = &http.Client{
		Transport: &http.Transport{
			Dial: func(network, addr string) (net.Conn, error) {
				deadline := time.Now().Add(1000 * time.Millisecond)
				C, err := net.DialTimeout(network, addr, 1000*time.Millisecond)
				if err != nil {
					return nil, err
				}
				C.SetDeadline(deadline)
				return C, nil
			},
		},
	}
	ac.addr = ClusterConfigSingleton.GetAddrByNodeName(nodeName)
}

func (ac *ApiClient) Do(url string, reqByte []byte) ([]byte, error) {
	reqReader, err := http.NewRequest("POST", url, strings.NewReader(string(reqByte)))
	if err != nil {
		return nil, err
	}
	rsp, err := ac.C.Do(reqReader)
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(rsp.Body)
}

func (ac *ApiClient) CallForVote(req *CallForVoteReq, rsp *CallForVoteRsp) (err error) {
	url := "http://" + ac.addr + HTTPPrefix + "/CallForVote"
	reqByte, err := json.Marshal(req)
	if err != nil {
		return err
	}
	rspByte, err := ac.Do(url, reqByte)
	if err != nil {
		glog.Error(err)
		return err
	}
	err = json.Unmarshal(rspByte, rsp)
	if err != nil {
		glog.Error(err)
		return err
	}
	return nil
}
func (ac *ApiClient) Beat(req *BeatReq, rsp *BeatRsp) (err error) {
	url := "http://" + ac.addr + HTTPPrefix + "/Beat"
	reqByte, err := json.Marshal(req)
	if err != nil {
		return err
	}
	rspByte, err := ac.Do(url, reqByte)
	if err != nil {
		glog.Error(err)
		return err
	}
	err = json.Unmarshal(rspByte, rsp)
	if err != nil {
		glog.Error(err)
		return err
	}
	return nil
}
func (ac *ApiClient) PullLog(req *PullLogReq, rsp *PullLogRsp) (err error) {
	url := "http://" + ac.addr + HTTPPrefix + "/PullLog"
	reqByte, err := json.Marshal(req)
	if err != nil {
		return err
	}
	rspByte, err := ac.Do(url, reqByte)
	if err != nil {
		glog.Error(err)
		return err
	}
	err = json.Unmarshal(rspByte, rsp)
	if err != nil {
		glog.Error(err)
		return err
	}
	return nil
}

// ------------------------------------------
// batch client

type SingleTypeBatchClient struct {
	clientArr []*ApiClient
	rspArr    []ApiRsp
	waitCh    chan int
	funcName  string
}

func (stbc *SingleTypeBatchClient) Init() {
	stbc.clientArr = make([]*ApiClient, 0)
	stbc.rspArr = make([]ApiRsp, 0)
}

func (stbc *SingleTypeBatchClient) Size() int {
	return len(stbc.clientArr)
}

func (stbc *SingleTypeBatchClient) Append(ac *ApiClient, apiRsp ApiRsp) {
	stbc.clientArr = append(stbc.clientArr, ac)
	stbc.rspArr = append(stbc.rspArr, apiRsp)
}

func (stbc *SingleTypeBatchClient) BeginToRun(apiReq ApiReq) {
	stbc.funcName = apiReq.GetName()
	stbc.waitCh = make(chan int, len(stbc.clientArr))
	for i := 0; i < len(stbc.clientArr); i++ {
		if stbc.funcName == "CallForVote" {
			go func(t int) {
				stbc.clientArr[t].CallForVote(apiReq.(*CallForVoteReq), stbc.rspArr[t].(*CallForVoteRsp))
				stbc.waitCh <- t
			}(i)
		}
		if stbc.funcName == "PullLog" {
			go func(t int) {
				stbc.clientArr[t].PullLog(apiReq.(*PullLogReq), stbc.rspArr[t].(*PullLogRsp))
				stbc.waitCh <- t
			}(i)
		}
		if stbc.funcName == "Beat" {
			go func(t int) {
				stbc.clientArr[t].Beat(apiReq.(*BeatReq), stbc.rspArr[t].(*BeatRsp))
				stbc.waitCh <- t
			}(i)
		}
	}
}

func (stbc *SingleTypeBatchClient) WaitAfterCount(count int) bool {
	var index int
	sum := 0
	glog.Infof("singleTypeBatchClient size = [%d]", stbc.Size())
	for i := 0; i < stbc.Size(); i++ {
		index = <-stbc.waitCh
		glog.Infof("singleTypeBatchClient for funcName = [%s], index = [%d]", stbc.funcName, index)
		if stbc.rspArr[index].GetState() == "OK" {
			sum++
		}
	}
	if sum >= count {
		return true
	} else {
		return false
	}
}
